package io.xccit.security.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.xccit.common.enums.ResultCodeEnum;
import io.xccit.common.result.AjaxResult;
import io.xccit.common.utils.IPUtil;
import io.xccit.common.utils.JwtHelper;
import io.xccit.common.utils.ResponseUtil;
import io.xccit.model.vo.LoginVo;
import io.xccit.security.service.ISysLoginLogService;
import io.xccit.security.util.CustomUserDetail;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author CH_ywx
 * @date 2023-06-27
 * @description 登录认证Filter
 */
public class CustomLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate<String,Object> redisTemplate;

    private ISysLoginLogService sysLoginLogService;

    public CustomLoginFilter(AuthenticationManager authenticationManager,
                             RedisTemplate<String,Object> redisTemplate,
                             ISysLoginLogService sysLoginLogService
                            ){
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式(这里注意：POST必须是大写，不然会报错)
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/system/admin/login","POST"));
        this.redisTemplate = redisTemplate;
        this.sysLoginLogService = sysLoginLogService;
    }

    /**
     * 认证方法
     * @param request from which to extract parameters and perform the authentication
     * @param response the response, which may be needed if the implementation has to do a
     * redirect as part of a multi-stage authentication process (such as OpenID).
     *
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            Authentication authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult the object returned from the <tt>attemptAuthentication</tt>
     * method. 认证成功方法
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //获取用户信息
        CustomUserDetail userDetail = (CustomUserDetail)authResult.getPrincipal();
        //生成Token
        String token = JwtHelper.createToken(userDetail.getSysUser().getId(), userDetail.getSysUser().getUsername());
        sysLoginLogService.recordLoginLog(userDetail.getUsername(), IPUtil.getIpAddress(request),0,"登录成功",new Date());
        //保存权限数据到redis
        redisTemplate.opsForValue().set(userDetail.getUsername(), JSON.toJSONString(userDetail.getAuthorities()));
        Map<String, Object> result = new HashMap<>();
        result.put("token",token);
        ResponseUtil.out(response, AjaxResult.ok(result));
    }

    /**
     * 认证失败方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        if (failed.getCause() instanceof RuntimeException){
            ResponseUtil.out(response,AjaxResult.build(204,failed.getMessage(),null));
        }else{
            ResponseUtil.out(response,AjaxResult.build(ResultCodeEnum.LOGIN_PARAM_ERROR,null));
        }
    }
}
